#include "socket.h"
#include <WS2tcpip.h>
#include "elassert.h"

using namespace el;

el::SocketAddrIn::SocketAddrIn()
{
	addr_.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addr_.sin_port = htons(0);
	addr_.sin_family = AF_INET;
	length = sizeof(sockaddr_in);
}

el::SocketAddrIn::SocketAddrIn(const sockaddr_in & addr)
{
	addr_ = addr;
	length = sizeof(sockaddr_in);
}

el::SocketAddrIn::SocketAddrIn(unsigned long ip, unsigned short port)
{
	addr_.sin_addr.S_un.S_addr = htonl(ip);
	addr_.sin_port = htons(port);
	addr_.sin_family = AF_INET;
	length = sizeof(sockaddr_in);
}

el::SocketAddrIn::SocketAddrIn(std::string ip, unsigned short port)
{
	
	addr_.sin_addr.s_addr = inet_addr(ip.c_str());
	addr_.sin_port = htons(port);
	addr_.sin_family = AF_INET;
	length = sizeof(sockaddr_in);
}

el::SocketAddrIn::SocketAddrIn(unsigned short port)
{
	addr_.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addr_.sin_port = htons(port);
	addr_.sin_family = AF_INET;
	length = sizeof(sockaddr_in);
}

el::SocketAddrIn::~SocketAddrIn()
{
}

std::string el::SocketAddrIn::getAddrString() const
{
	char buf[256] = { 0 };
	IN_ADDR sin_addr = addr_.sin_addr;
	char* p = inet_ntoa(sin_addr);
	int port = ntohs(addr_.sin_port);
	sprintf(buf, "%s:%d", p, port);
	return std::string(buf);
}

const sockaddr_in & el::SocketAddrIn::sockaddr() const
{
	return addr_;
}

sockaddr_in& el::SocketAddrIn::sockaddr()
{
	return addr_;
}

int el::SocketAddrIn::lenght() const
{
	return length;
}

int& el::SocketAddrIn::lenght()
{
	return length;
}

unsigned short el::SocketAddrIn::getPort()
{
	return ntohs(addr_.sin_port);
}

std::string el::SocketAddrIn::getIP()
{
	return inet_ntoa(addr_.sin_addr);
}

bool el::SocketAddrIn::sameAddr(SocketAddrIn& addr)
{
	return getIP() == addr.getIP() &&
		getPort() == addr.getPort();
}

FD el::Socket::getTcpSocket()
{
	return ::socket(AF_INET, SOCK_STREAM, 0);
}

FD el::Socket::getUdpSocket()
{
	return ::socket(AF_INET, SOCK_DGRAM, 0);
}

int el::Socket::getSocketError(FD fd)
{
	int opt = 0;
	int len = sizeof(opt);
	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&opt, &len) < 0)
	{
		return WSAGetLastError();
	}
	return opt;
}

Socket::Socket(FD fd)
	:fd_(fd)
{
	
}

Socket::~Socket()
{
}

FD el::Socket::fd()
{
	return fd_;
}

int el::Socket::bind(const SocketAddrIn & addr)
{
	return ::bind(fd_, (sockaddr*)&addr.sockaddr(), addr.lenght());
}

int el::Socket::listen(int backlog)
{
	return ::listen(fd_, backlog);
}

int el::Socket::connect(const SocketAddrIn & addr)
{
	return ::connect(fd_, (sockaddr*)&addr.sockaddr(), addr.lenght());
}

FD el::Socket::accept(SocketAddrIn & addr)
{
	return ::accept(fd_, (sockaddr*)&addr.sockaddr(), &addr.lenght());
}

int el::Socket::reuserAddr(bool b)
{
	int opt = b;
	return setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
}

int el::Socket::nonBlockSocket(bool b)
{
	unsigned long opt = b;
	return ioctlsocket(fd_, FIONBIO, &opt);
}

int el::Socket::tcpNonDelay(bool b)
{
	int opt = b;
	return setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(opt));
}

int el::Socket::tcpKeepAlive(bool b)
{
	int opt = b;
	return setsockopt(fd_, SOL_SOCKET, SO_KEEPALIVE, (const char*)&opt, sizeof(opt));
}

int el::Socket::tcpNodelay(bool b)
{
	int opt = b;
	return setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(opt));
}

int el::Socket::shutdownRead()
{
	return shutdown(fd_, SD_RECEIVE);
}

int Socket::shutdownWrite()
{
	return shutdown(fd_, SD_SEND);
}

int el::Socket::getSocketError()
{
	return getSocketError(fd_);
}

int el::Socket::closeFD()
{
	return ::closesocket(fd_);
}

bool el::Socket::isSelfConnect()
{
	return false;
}
